package first.endtoend.webservices.mobile

import first.endtoend.*
import first.endtoend.messages.MessageCode
import grails.converters.JSON
import groovy.json.JsonSlurper

import java.text.SimpleDateFormat

/*
@author : Olfa

this webservice is invoked by an Agent
families : render a JSON of families related to this Agent (one FPS)
categories : render a JSON of all categories in DB
aids : render a JSOn of aids related to familyList of this agent
products : render a JSON of all products in DB
 */

class WebserviceAgentController
{
    def familyService
    Agent agentInstance
    def springSecurityService
    def beforeInterceptor = [action: this.&init]
    def categoryService
    def productService
    def aidService
    def listFamilies
    Boolean verifySession
    String regId
    /*
    listSessions to store history of sessionId of an agent
   */

    /**
     *
     * @return
     */
    def init()
    {


        try
        {
            agentInstance = springSecurityService.currentUser as Agent
            regId = params?.regId
        } catch (Exception exp)
        {

            log.info("User is not an agent!!")
            render([code: MessageCode.NOT_FOUND.value()] as JSON)
        }


    }

    /**
     *
     * @return
     * this method render all families related to connected agent code 200 if success
     if there is no family for this age, it render code 501
     if the user is not an agent , it renders code 400
     */
/*
    def familiesByFPS() {
        log.info("\n________Webservice Families By FPS\n Agent = " + agentInstance.toString()
        def listResult = new ArrayList<Family>()
        listFamilies = new ArrayList<Family>()
        //get FPS of the current agent
        FairPricesShop fpsInstance = agentInstance.fps
        println"he FPS is = " + fpsInstance.toString()
        if (fpsInstance) {
            // listFamilies=Family.executeQuery("select f from Family f where f.rationCard IS NOT NULL and f.fps=?",fpsInstance)
            listFamilies = Family.findAllByFps(fpsInstance)
            log.info("these are families for this shop : " + listFamilies)
        } else {
            log.info("There is not an fps for this agent"
        }
        if (listFamilies != null && listFamilies.size() != 0) {
            log.info("Families and shop^: "
            listFamilies.each {
                //   Object obj=it.asType(FamilyView1.class)
                //   def jsonFamilyTest= JsonHelper.createJsonObject(obj)

                //   log.info("****here json family test "+jsonFamilyTest)
                listResult.add(familyService.toJson(it))
            }
            log.info("***" + listFamilies)
            render([code: MessageCode.SUCCESS.value(), listFamilies: listResult, count: listResult.size()] as JSON)

        } else {
            render([code: MessageCode.NULL_RESPONSE.value(), count: listResult.size()] as JSON)

        }

    }
*/

    def families()
    {
        log.info("families webservice with agent : " + agentInstance?.toString())
        def listResult = new ArrayList<Family>()
        ArrayList<Family> listFamilies = new ArrayList<Family>()
        //get FPS of the current agent
        FairPricesShop fpsInstance = agentInstance.fps
        if (fpsInstance)
        {
            // listFamilies=Family.executeQuery("select f from Family f where f.rationCard IS NOT NULL and f.fps=?",fpsInstance)
            listFamilies.addAll(Family.findAllByRationCardIsNotNullAndFps(fpsInstance))
            log.info("these are families for this shop : " + listFamilies)
        } else
        {
            //there is not an fps for this agent
        }
        if (listFamilies != null && listFamilies.size() != 0)
        {
            listFamilies.each {
                /*   Object obj=it.asType(FamilyView1.class)
                   def jsonFamilyTest= JsonHelper.createJsonObject(obj)

                   log.info("****here json family test "+jsonFamilyTest)*/
                listResult.add(familyService.toJson(it))
            }
            log.info("***" + listFamilies)
            render([code: MessageCode.SUCCESS.value(), listFamilies: listResult, count: listResult.size()] as JSON)

        } else
        {
            render([code: MessageCode.NULL_RESPONSE.value(), count: listResult.size()] as JSON)

        }

    }

    /**
     *
     * @return this method render aids filtered by categories related to families of the requester agent

     */
    def aids()
    {
        log.info("\n____________________________\nWebservice Aids")
        if (agentInstance != null)
        {
            log.info("Agent = " + agentInstance.toString())
            def listResult = new ArrayList<Aid>()
            def allAids = new ArrayList<Aid>()
            def allDistinctCategories = new ArrayList<Category>()

            Aid aidInstance

            //here this sql Req returns distinct categories from table family and with a selection of families for this agent
            allDistinctCategories = Family.executeQuery("select distinct f.category from Family f, FairPricesShop fp, Agent a where f.fps.id = fp.id AND a.fps.id = fp.id AND a.id=?", [agentInstance.id])
            log.info("fps = " + agentInstance.fps)
            log.info("allDistinctCategories = " + allDistinctCategories.toString())
            def aidsByCategory
            allDistinctCategories.each {
                log.info("Categorie = " + it.toString())
                aidsByCategory = Aid.findAllByCategory(it)
                aidsByCategory.each {
                    log.info("aidByCategory " + it.toString())
                    allAids.add(it)
                }

            }
            log.info("these are allAids" + allAids)
            if (allAids.size() != 0)
            {
                allAids.each {
                    listResult.add(aidService.toJson(it))
                }
                log.info("all aids : " + listResult)
                render([code: MessageCode.SUCCESS.value(), listAids: listResult, count: listResult.size()] as JSON)

            } else
            {
                render([code: MessageCode.NULL_RESPONSE.value(), count: listResult.size()] as JSON)

            }
        } else
        {
            render([code: "Agent not found"] as JSON)
        }

    }

/**
 *
 * @return this method return all existing categories in DB

 */
    def categories()
    {

        log.info("\n_______Webservice Categories\nagent = " + agentInstance.toString())

        def listCategories = Category.findAll()
        def listResult = new ArrayList<Category>()

        if (listCategories.size())
        {  //if there is categories in Database (DB has been already initialised
            log.info("Il y a " + listCategories.size() + " categorie(s)")

            listCategories.each {

                if (categoryService.toJson(it) != null)
                {
                    listResult.add(categoryService.toJson(it))

                } else
                {

                }
                log.info("###" + listResult)
            }

            if (listResult.size() != 0)
            {
                render([code: MessageCode.SUCCESS.value(), listCategories: listResult, count: listResult.size()] as JSON)

            } else
            {
                render([code: MessageCode.NULL_RESPONSE.value(), listCategories: listResult, count: listResult.size()] as JSON)

            }

        } else
        {
            render([code: MessageCode.NULL_RESPONSE.value(), count: listResult.size()] as JSON)

        }


    }
    /**
     *
     @return this method return all existing products in DB
     */
    def products()
    {
        log.info("\n__________________\nWebservice Products\nAgent =  " + agentInstance.toString())
        def lisProducts = Product.findAll()
        def listResult = new ArrayList<Product>()

        if (lisProducts)
        {
            log.info("Liste des produits sur le serveur")
            lisProducts.each {
                listResult.add(productService.toJson(it))
            }
            render([code: MessageCode.SUCCESS.value(), listProducts: listResult, count: listResult.size()] as JSON)
        } else
        {
            render([code: MessageCode.NULL_RESPONSE.value(), count: listResult.size()] as JSON)

        }


    }

    /**
     *
     * @return this method render a family to agent

     */
    def family()
    {
        log.info("\n___________\nWebservices Family\nAgent = " + agentInstance.toString())
        def jsonNotif = params?.notif
        log.info("Notificatio json de l'agent " + jsonNotif)
        log.info(params)
        if (jsonNotif == null)
        {
            render([code: MessageCode.MALFORMED_REQUEST.value()] as JSON)
            return
        }
        NotificationView notif = JsonHelper.getObjectFromJson(jsonNotif, NotificationView.class) as NotificationView
        log.info("NotifView = " + notif.toString())
        List<Long> list = notif.data
        List<Family> listFamily = new ArrayList<Family>()
        Family familyInstance
        def result = new ArrayList<Family>()
        log.info("Pour chaque Notif")
        list.each {
            familyInstance = Family.findById(it)
            if (familyInstance)
            {
                familyInstance.toString()
                listFamily.add(familyInstance)
            } else
            {
            }
        }
        listFamily.each {
            result.add(familyService.toJson(it))
        }
        if (result)
        {
            render([code: MessageCode.SUCCESS.value(), listFamilies: result] as JSON)

        } else
        {
            render([code: MessageCode.NULL_RESPONSE.value(), error: "families to update don't exist on Server DB"] as JSON)
        }
    }

/**
 *
 * @return this method render a product to agent

 */

    def product()
    {
        log.info("\n______________\n Webservice Product\n Agent = " + agentInstance.toString())
        def jsonNotif = params?.notif
        log.info("Notificatio json de l'agent " + jsonNotif)
        if (jsonNotif == null)
        {
            render([code: MessageCode.MALFORMED_REQUEST.value()] as JSON)
            return
        }
        NotificationView notif = JsonHelper.getObjectFromJson(jsonNotif, NotificationView.class) as NotificationView
        log.info("NotifView = " + notif.toString())
        List<Long> list = notif.data
        List<Product> listProduct = new ArrayList<Product>()
        Product productInstance
        def result = new ArrayList<Product>()
        log.info("Pour chaque notif")
        list.each {
            productInstance = Product.findById(it)
            productInstance.toString()
            listProduct.add(productInstance)
        }
        listProduct.each {
            result.add(productService.toJson(it))
        }

        if (result)
        {
            render([code: MessageCode.SUCCESS.value(), listProducts: result] as JSON)

        } else
        {
            render([code: MessageCode.NULL_RESPONSE.value(), error: "products to update don't exist on Server DB"] as JSON)

        }

    }
    /**
     *
     * @return this method render an aid to agent

     */

    def aid()
    {
        log.info("\n________\nWebservice Aid\n Agent = " + agentInstance.toString())
        def jsonNotif = params?.notif
        log.info("Notificatio json de l'agent " + jsonNotif)
        if (jsonNotif == null)
        {
            render([code: MessageCode.MALFORMED_REQUEST.value()] as JSON)
            return
        }
        NotificationView notif = JsonHelper.getObjectFromJson(jsonNotif, NotificationView.class) as NotificationView
        log.info("NNotifView " + notif)
        List<Long> list = notif.data
        List<Aid> listAid = new ArrayList<Aid>()
        Aid aidInstance
        def result = new ArrayList<Aid>()
        log.info("Pour chaque notifs")
        list.each {
            aidInstance = Aid.findById(it)
            log.info(aidInstance.toString())
            listAid.add(aidInstance)
        }
        /*  Category categoryInstance
          Family familyInstance*/
        def families = new ArrayList<Family>()
        listAid.each {
            result.add(aidService.toJson(it))
        }

        log.info("---> all aids to send after notif : " + result)

        if (result)
        {
            render([code: MessageCode.SUCCESS.value(), listAids: result] as JSON)

        } else
        {
            render([code: MessageCode.NULL_RESPONSE.value(), error: "aids to update don't exist on Server DB"] as JSON)
        }
    }

/**
 *
 * @return this method get RegId of a registred device to GCM-server

 */
    def getRegId()
    {
        log.info("\n_________________\nWebservice getRegID\nAgent = " + agentInstance.toString())
        if (regId != null)
        {
            log.info("regId is no null and will be updated : " + regId)
            agentInstance.regId = regId
            agentInstance.save()
            render([code: MessageCode.SUCCESS.value()] as JSON)
        } else
        {
            render([code: MessageCode.NOT_FOUND.value()] as JSON)
        }
    }

    /**
     * Make a transaction between an agent and a family.
     * POST method
     *
     * @param familyID ID of the family
     * @param productName name of the product of the transaction
     * @param quantityRecovered the new quantity
     * @param longitude Longitude where the transaction has been made
     * @param latitude Lattitude where the transaction has been made
     * @param precision Precision of the GPS location
     * @param image Bytes of the picture
     *{"precision":0,"traceDetails":[{"id":0,"product":{"id":1},"quantityRecovered":2}],"agentId":2,"longitude":0,"pathPhoto":"",
     * "latitude":0,"beneficiary":{"id":11,"family":{"id":5}},"dateOp":"/Date(1430149688854)/"}
     */
    def transaction()
    {
        log.info("\nWebservice TRANSACTION")
        Date currentDate = new Date(System.currentTimeMillis())
        Family family
        PortfolioDetail pfd
        Trace trace

        log.info("Webservice = " + request.JSON)
        JsonSlurper jsonSlurper = new JsonSlurper()
        def txt = request.JSON.toString()
        def json = jsonSlurper.parseText(txt)
        json instanceof Map
        json.beneficiary.family.id.class == Long
        json.traceDetails.class == List
        json.traceDetails.product.id.class == Long
        json.traceDetails.quantityRecovered.class == Float
        json.longitude.class == Double
        json.latitude.class == Double
        json.precision.class == Double
        json.pathPhoto.class == Byte[]
        json.dateOp.class == java.sql.Date
        json.agentId.class == Long

        try
        {
            agentInstance = Agent.findById(json.agentId)
            if (agentInstance == null)
            {
                log.error("AGENT is " + agentInstance.toString() + "Family Not found\nStacktrace\n" + e.printStackTrace())
                render([code: MessageCode.FAIL_RETRIEVE.value(), message: "No Agent"])
                return
            }

        } catch (Exception e)
        {
            log.error("AGENT " + agentInstance.toString() + "Agent not found\nStacktrace\n" + e.printStackTrace())
            render([code: MessageCode.FAIL_RETRIEVE.value(), message: "No Agent"])
            return
        }
        try
        {
            family = Family.findById(json.beneficiary.family.id)

        } catch (Exception e)
        {
            log.error("FAMILY " + family.toString() + "Family Not found\nStacktrace\n" + e.printStackTrace())
            render([code: MessageCode.FAIL_RETRIEVE.value(), message: "Family not found, familyID not correct or Family not in database"])
            return
        }

        log.info("LONGITUDE = " + json.longitude + " LATITUDE = " + json.latitude + " PRECISION = " + json.precision)
        def pathPhoto = formatDate(currentDate) + "-" + family.getFamilyName() + ".jpg"
        log.info("PATH = " + pathPhoto)
        this.writePhotoFromWS(json.pathPhoto, pathPhoto)
        trace = new Trace(dateCreated: currentDate, dateOp: currentDate, family: family,
                longitude: json.longitude, latitude: json.latitude, precision: json.precision,
                agent: agentInstance, pathPhoto: pathPhoto).save(flush: true)
        log.info("Trace = " + trace.toString())
        List traces = json.traceDetails
        for (item in traces)
        {
            Product product
            TraceDetail traceDetail
            try
            {
                product = Product.findById(item.product.id)
            } catch (Exception e)
            {
                log.error("PRODUCT " + product.toString() + "Product Non found\nStacktrace\n" + e.printStackTrace())
                render([code: MessageCode.FAIL_RETRIEVE.value(), message: "Product not found, ProductID not correct or Product not in database"])
                return
            }
            try
            {
                traceDetail = new TraceDetail(qteRecovered: item.quantityRecovered, product: product)
                log.info("Trace Details = " + traceDetail.toString())
            } catch (Exception e)
            {
                log.error("TraceDetails is not correct" + traceDetail?.toString() + "\n" +
                        "Stacktrace\n" +
                        "\" + e.printStackTrace()")
                render([code: MessageCode.FAIL.value(), message: "TraceDetails is not correct"])
                return
            }
            try
            {
                pfd = PortfolioDetail.findByProductAndPortfolio(product, family.portfolio)
            } catch (Exception e)
            {
                log.error("PortFolioDetails " + pfd?.toString() + "is not correct" + "\nStacktrace\n" + e.printStackTrace())
                render([code: MessageCode.FAIL.value(), message: "PortFolioDetails error, values may not be correct or product is not in this portfolio"])
                return

            }
            if (pfd.quantity > item.quantityRecovered || pfd.quantity >= 0)
            {
                try
                {
                    pfd.quantity -= item.quantityRecovered
                } catch (Exception e)
                {
                    log.error("PRODUCT " + product.toString() + "PDS quantity = " + pfd?.quantity + " Quantity Transaction = " + item?.quantityRecovered + "\nStacktrace\n" + e.printStackTrace())
                    render([code: MessageCode.FAIL.value(), message: "PortFolioDetails Quantity is lower than tansaction amount or quantity is below or equal zero"])
                    return
                }
            }
            log.info("Saving traces et portfoliodetails")
            pfd.save(flush: true)
            traceDetail.save(flush: true)
            trace.addToDetails(traceDetail).save(flush: true)
        }

        render([code: MessageCode.SUCCESS.toString(), message: trace.toString()] as JSON)
    }

    def writePhotoFromWS(def bytes, String imageName)
    {
        String dest = servletContext.getRealPath("/") + "/images/tracesPhoto/" + imageName
        log.info("Destination == " + dest)
        File file = new File(dest).write(bytes)
    }

    def formatDate(Date oldDate)
    {
        SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yy-")
        return sdf.format(oldDate)
    }

}

